package com.jplus.core.mvc;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jplus.core.utill.JSON;
import com.jplus.core.utill.JUtil;
import com.jplus.core.utill.StreamUtil;

/**
 * Web 操作工具类,依赖于WebContext 注重数据交互
 */
public class WebUtil {
	private static final Logger logger = LoggerFactory.getLogger(WebUtil.class);
	private static final String CHARSET = Charset.defaultCharset().name();

	/**
	 * 将数据以 JSON 格式写入响应中
	 */
	public static void writeJSON(Object data) {
		String json = JSON.toJSONString(data);
		write("application/json", json);
	}

	/**
	 * 将数据以 HTML 格式写入响应中
	 */
	public static void writeHTML(String data) {
		write("text/html", data);
	}

	/**
	 * 将数据以 TEXT 格式写入响应中
	 */
	public static void writeTEXT(String data) {
		write("text/plain", data);
	}

	public static void write(String ContentType, String data) {
		try {
			HttpServletResponse response = WebContext.getResponse();
			// 设置响应头
			response.setContentType(ContentType); // 指定内容类型为 HTML 格式
			response.setCharacterEncoding(CHARSET); // 防止中文乱码
			// 向响应中写入数据
			PrintWriter writer = response.getWriter();
			writer.write(data);
			writer.flush();
			writer.close();
		} catch (Exception e) {
			logger.error("在响应中写数据出错！", e);
			throw new RuntimeException(e);
		} finally {
			logger.info("[>> Response]:{}", data);
		}
	}

	/**
	 * 从请求中获取所有参数（当参数名重复时，用后者覆盖前者）
	 */
	public static <T> T getRequestParamBean(Class<T> clazz) throws Exception {
		return (T) JSON.parseBean(JSON.toJSONString(getRequestParamMap()), clazz);
	}

	/**
	 * 从请求中获取所有参数（当参数名重复时，用后者覆盖前者）
	 */
	public static Map<String, Object> getRequestParamMap() {
		Map<String, Object> paramMap = new LinkedHashMap<String, Object>();
		try {
			HttpServletRequest request = WebContext.getRequest();
			Enumeration<String> paramNames = request.getParameterNames();
			while (paramNames.hasMoreElements()) {
				String paramName = paramNames.nextElement();
				if (checkParamName(paramName)) {
					String[] paramValues = request.getParameterValues(paramName);
					if (paramValues != null) {
						if (paramValues.length == 1) {
							paramMap.put(paramName, paramValues[0]);
						} else {
							StringBuilder paramValue = new StringBuilder("");
							for (int i = 0; i < paramValues.length; i++) {
								paramValue.append(paramValues[i]);
								if (i != paramValues.length - 1) {
									paramValue.append(JUtil.SEPARATOR);
								}
							}
							paramMap.put(paramName, paramValue.toString());
						}
					}
				}
			}
		} catch (Exception e) {
			logger.error("获取请求参数出错！", e);
			throw new RuntimeException(e);
		}
		return paramMap;
	}

	private static boolean checkParamName(String paramName) {
		return !paramName.equals("_"); // 忽略 jQuery 缓存参数
	}

	/**
	 * 转发请求
	 */
	public static void forwardRequest(String path) {
		try {
			HttpServletRequest request = WebContext.getRequest();
			HttpServletResponse response = WebContext.getResponse();
			path = JUtil.formatPath(path);
			request.getRequestDispatcher(path).forward(request, response);
		} catch (Exception e) {
			logger.error("转发请求出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 重定向请求
	 */
	public static void redirectRequest(String path) {
		try {
			HttpServletResponse response = WebContext.getResponse();
//			path = JUtil.formatPath(path);
			response.sendRedirect(path);
		} catch (Exception e) {
			logger.error("重定向请求出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 发送错误代码
	 */
	public static void sendError(int code, String message) {
		logger.error("Request ERROR:" + message);
		try {
			HttpServletResponse response = WebContext.getResponse();
			response.sendError(code, message);
		} catch (Exception e) {
			logger.error("发送错误代码出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 判断是否为 AJAX 请求
	 */
	public static boolean isAJAX() {
		return WebContext.getRequest().getHeader("X-Requested-With") != null;
	}

	/**
	 * 获取请求路径
	 */
	public static String getRequestPath() {
		HttpServletRequest request = WebContext.getRequest();
		String servletPath = request.getServletPath();
		String pathInfo = JUtil.toString(request.getPathInfo());
		return servletPath + pathInfo;
	}

	/**
	 * 从 Cookie 中获取数据
	 */
	public static String getCookie(String name) {
		String value = "";
		try {
			HttpServletRequest request = WebContext.getRequest();
			Cookie[] cookieArray = request.getCookies();
			if (cookieArray != null) {
				for (Cookie cookie : cookieArray) {
					if (!JUtil.isEmpty(name) && name.equals(cookie.getName())) {
						value = URLDecoder.decode(cookie.getValue(), CHARSET);
						break;
					}
				}
			}
		} catch (Exception e) {
			logger.error("获取 Cookie 出错！", e);
			throw new RuntimeException(e);
		}
		return value;
	}

	/**
	 * 下载文件
	 */
	public static void downloadFile(String filePath) {
		try {
			HttpServletResponse response = WebContext.getResponse();
			String path = JUtil.formatPath(filePath);
			String originalFileName = path.substring(path.lastIndexOf("/"));
			String downloadedFileName = new String(originalFileName.getBytes("GBK"), "ISO8859_1"); // 防止中文乱码

			response.setContentType("application/octet-stream");
			response.addHeader("Content-Disposition", "attachment;filename=\"" + downloadedFileName + "\"");

			InputStream inputStream = new BufferedInputStream(new FileInputStream(filePath));
			OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
			StreamUtil.toCopy(inputStream, outputStream);
		} catch (Exception e) {
			logger.error("下载文件出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 上传文件 必要条件： html: <form method="post" enctype="multipart/form-data">...</form>
	 * servlet:3.0+
	 */
	// public static InputStream uploadFile(String fileName){
	//
	// }
	/**
	 * 设置 Redirect URL 到 Session 中
	 */
	public static void setRedirectUrl(String sessionKey) {
		if (!isAJAX()) {
			String requestPath = getRequestPath();
			WebContext.getSession().setAttribute(sessionKey, requestPath);
		}
	}

	/**
	 * 是否为 IE 浏览器
	 */
	public boolean isIE() {
		String agent = WebContext.getRequest().getHeader("User-Agent");
		return agent != null && agent.contains("MSIE");
	}

	/**
	 * 本机IP
	 */
	public static String getLocalRealIp() {
		try {
			return InetAddress.getLocalHost().getHostAddress().toString();
		} catch (Exception e) {
			logger.error("获取本机IP异常", e);
			return "127.0.0.1";
		}
	}

	/**
	 * @param request
	 * @return 请求IP地址
	 */
	public static String getRequstIp() {
		String ip = null;
		HttpServletRequest request = WebContext.getRequest();
		ip = request.getHeader("X-Forwarded-For");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("Proxy-Client-IP");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("WL-Proxy-Client-IP");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("HTTP_CLIENT_IP");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getParameter("__fromReferIP");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("X-Real-IP");// NGINX用
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getRemoteAddr();
		return ip;
	}

	private static boolean isRealIP(String ip) {
		return !JUtil.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip);
	}

	private static String getRealIp(String ip) {
		if (String.valueOf(ip).indexOf(",") != -1) {
			return JUtil.getLeft(ip.split(",")[0], 15);
		}
		return ip;
	}
}
